#include <crtdbg.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include "alp_string.h"
#include "bencode.h"
static char * parse_string(size_t *size, const char *buf, size_t len)
{
	size_t n;
	char *p = (char *)memchr(buf, ':', len);
	if (p == 0)
		return 0;
	n = p - buf;
	if (n >= len)
		return 0;
	if (alp_strntoz(size, buf, n))
		return 0;
	len -= (++p - buf);
	if (len < *size)
		return 0;
	return p;	
}

alp_status_t bencode_parse(bencode_node_t **node, const char *buf, size_t *len)
{
	bencode_node_t * new_node = (bencode_node_t*)malloc(sizeof(bencode_node_t));
	*node = 0;
	if (new_node == 0)
		return ALP_E_MEMORY;
	switch (*buf)
	{
	case 'd':
	{
		alp_dict_t *dict;
		bencode_node_t *child;
		size_t klen;
		const char *key;
		ALP_CHECK(alp_dict_create(&dict));
		new_node->type = BENCODE_DICT;
		new_node->dict = dict;
		for (++buf, --*len; *buf != 'e' && *len; )
		{
			size_t n;
			key = parse_string(&klen, buf, *len);
			if (key == 0)
				return ALP_E_INPUT;
			*len -= (key + klen - buf);			
			buf = key + klen;
n = *len;
			ALP_CHECK(bencode_parse(&child, buf, len));
				buf += (n - *len);
			ALP_CHECK(alp_dict_set(dict, key, klen, child));			
		}
		if (*len == 0)
			return ALP_E_INPUT;
		--*len; // skip 'e'
		break;
	}
	case 'l':
	{
		alp_list_t *list;
		bencode_node_t *child;
		ALP_CHECK(alp_list_create(&list));
		new_node->type = BENCODE_LIST;
		new_node->list = list;
		for (++buf, --*len; *buf != 'e' && *len; )
		{
			size_t n = *len;
			ALP_CHECK(bencode_parse(&child, buf, len));
			buf += (n - *len);
			ALP_CHECK(alp_list_append(list, child));
		}
		if (*len == 0)
			return ALP_E_INPUT;
		--*len; // skip 'e'
		break;
	}
	case 'i':
	{
		size_t size;
		const char *p = (const char *)memchr(++buf, 'e', --*len);
		if (p == 0)
			return ALP_E_INPUT;
		size = p - buf;
		new_node->type = BENCODE_INT;
		new_node->buffer = alp_memdup(buf, size);
		new_node->length = size;
		*len -= (size + 1); // skip 'e'
		break;
	}
	default:
	{
		size_t size;
		char *p = parse_string(&size, buf, *len);
		if (p == 0)
			return ALP_E_INPUT;
		new_node->type = BENCODE_STRING;
		new_node->buffer = alp_memdup(p, size);
		new_node->length = size;
		*len -= (p + size - buf);
		break;
	}
	}
	*node = new_node;
	return 0;
}

alp_status_t alp_bencode_destroy(bencode_node_t *node)
{
	switch(node->type )
	{
	case BENCODE_INT:
		free(node->buffer );
		break;
	case BENCODE_STRING:
		free(node->buffer);
		break;
	case BENCODE_LIST:
		alp_list_destroy(node->list);
		break;
	case BENCODE_DICT:
		alp_dict_destroy(node->dict);
		break;
	default:
		return ALP_E_INPUT;
	}
	free(node);
	return 0;
}

#define BENCODE_CALLBACK(buf, len) ALP_CHECK(callback(userdata, buf, len))

static alp_status_t dump_string(const char *buf, size_t len, void *userdata, alp_status_t (*callback)(void *userdata, const char *buf, size_t len))
{
	if (len == 0)
		return callback(userdata, "0:", 2);
	else
	{
		char s[64];
		char *p = s + sizeof(s);
		size_t n;
		for (n = len; n; n /= 10)
			*--p = (char)(n % 10) + '0';
		BENCODE_CALLBACK(p, s + sizeof(s) - p);
		BENCODE_CALLBACK(":", 1);
		BENCODE_CALLBACK(buf, len);
	}
	return 0;
}

alp_status_t bencode_dump(bencode_node_t *node, void *userdata, alp_status_t (*callback)(void *userdata, const char *buf, size_t len))
{
	switch (node->type)
	{
	case BENCODE_INT:
		BENCODE_CALLBACK("i", 1);
		BENCODE_CALLBACK(node->buffer, node->length);
		BENCODE_CALLBACK("e", 1);
		break;
	case BENCODE_STRING:
	{
		ALP_CHECK(dump_string(node->buffer, node->length, userdata, callback));
		break;
	}
	case BENCODE_LIST:
	{
		alp_list_index_t *li;
		BENCODE_CALLBACK("l", 1);
		for (li = alp_list_first(node->list); li; li = alp_list_next(li))
		{
			bencode_node_t *child;
			alp_list_this(li, (void **)&child);
			ALP_CHECK(bencode_dump(child, userdata, callback));
		}
		BENCODE_CALLBACK("e", 1);
		break;
	}
	case BENCODE_DICT:
	{
		alp_dict_index_t *di;
		BENCODE_CALLBACK("d", 1);
		for (di = alp_dict_first(node->dict); di; di = alp_dict_next(di))
		{
			const char *key;
			size_t klen;
			bencode_node_t *child;
			alp_dict_this(di, (const void **)&key, &klen, (void **)&child);
			ALP_CHECK(dump_string(key, klen, userdata, callback));
			ALP_CHECK(bencode_dump(child, userdata, callback));
		}
		BENCODE_CALLBACK("e", 1);
		break;
	}
	default:
		return ALP_E_INPUT;
	}
	return 0;
}

#undef BENCODE_CALLBACK

alp_status_t myprintf(void* userdata, const char* buf, size_t len)
{
	char* temp;
	temp = (char*) malloc(len+1);
	memcpy(temp,buf,len);
	temp[len] = 0;
	printf("%s\n",temp);
	free(temp);
	return 0;
}
/*for test
int main()
{
	bencode_node_t * node;
	FILE * f;
	char* buffer;
	size_t len = 44307;
	int tmp;
//orz boost
	tmp = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
	tmp |= _CRTDBG_LEAK_CHECK_DF;
	_CrtSetDbgFlag(tmp);

	f = fopen("C:\\1.torrent","rb");
	buffer = (char*) malloc(44307+100);
	fread(buffer, 1, 44307, f);
	fclose(f);
	bencode_parse(&node, buffer, &len);
	bencode_dump(node,NULL,myprintf);
	alp_bencode_destroy(node);
	
	return 0;
}

*/
